netutils/dropbear: initial Dropbear SSH server port for NuttX#3532
netutils/dropbear: initial Dropbear SSH server port for NuttX#3532FelipeMdeO wants to merge 2 commits into
Conversation
eae68b0 to
8778f48
Compare
|
@FelipeMdeO nice work! Kudos!!! Some suggestions to improve this PR:
|
| for (i = 0; i < len; i++) | ||
| ninfo("Starting the Dropbear SSH server\n"); | ||
|
|
||
| ret = nsh_parse(&pstate->cn_vtbl, cmdline); |
There was a problem hiding this comment.
Why to use it instead of doing like Telnet does: "nsh_session(pstate, NSH_LOGIN_TELNET, argc, argv);" ?
There was a problem hiding this comment.
Hello Alan, nsh_dropbearstart() mirrors nsh_telnetstart() (nsh_telnetd.c), not nsh_telnetmain(). This is a bit tricky, but let me try to explain.
SSH can't reuse the nsh_session() path: unlike Telnet's line protocol, each SSH connection multiplexes channels and the shell must run on a PTY (for SIGINT/Ctrl-C, window size, raw mode). So the per-session NSH is spawned inside the Dropbear app (dropbear_nshsession.c) over a PTY pair, with no equivalent nsh_session() call at the nshlib layer.
We have this diff "approach" because telnet can hand the socket straight to nsh_session() because it's a plain-text line protocol while SSH transfer data uses encrypted data.
| #define DISABLE_LASTLOG 1 | ||
| #define DISABLE_PAM 1 | ||
| #define DISABLE_PUTUTLINE 1 | ||
| #define DISABLE_PUTUTXLINE 1 | ||
| #define DISABLE_SYSLOG 1 | ||
| #define DISABLE_UTMP 1 | ||
| #define DISABLE_UTMPX 1 | ||
| #define DISABLE_WTMP 1 | ||
| #define DISABLE_WTMPX 1 | ||
| #define DISABLE_ZLIB 1 |
There was a problem hiding this comment.
I think these variable could be controlled from Kconfig definitions
There was a problem hiding this comment.
Hello Alan, good point. Most of that DISABLE_* block isn't really configurable, for example: DISABLE_PAM, DISABLE_LASTLOG, but other configs can be controlled by user, like CONFIG_LIB_ZLIB.
I will move to Kconfig what can be controlled by user.
| #define HAVE_STRUCT_ADDRINFO 1 | ||
| #define HAVE_STRUCT_IN6_ADDR 1 | ||
| #define HAVE_STRUCT_SOCKADDR_IN6 1 | ||
| #define HAVE_STRUCT_SOCKADDR_STORAGE 1 | ||
| #define HAVE_STRUCT_SOCKADDR_STORAGE_SS_FAMILY 1 |
There was a problem hiding this comment.
Some of these definitions cannot be hard-code, i.e.: HAVE_STRUCT_SOCKADDR_IN6, HAVE_STRUCT_SOCKADDR_IN6, could be disabled if IPv6 support on NuttX is not enabled.
There was a problem hiding this comment.
About HAVE_STRUCT_SOCKADDR_IN6:
The fake-rfc2553.h dropbear file has the source code below:
#ifndef HAVE_STRUCT_IN6_ADDR
struct in6_addr {
u_int8_t s6_addr[16];
};
#endif /* !HAVE_STRUCT_IN6_ADDR */
The issue is that the nuttx has the same struct in6_addr, so I need "disable" this code sector to be able compile.
Look NuttX file netinet/in.h, it always define s6_addr:
#define s6_addr in6_u.u6_addr8
#define s6_addr16 in6_u.u6_addr16
#define s6_addr32 in6_u.u6_addr32
I am using this macro equal 1 because s6_addr always exist in nuttx.
0cc194f to
49a5822
Compare
49a5822 to
9c4ff8e
Compare
|
I am reviewing your comments and doing improvement in the source code, I will ping you all to review again in the next days. tks. |
Hello @acassis, @xiaoxiang781216. Could you please confirm whether you would like me to contact the original author? I am concerned about his position regarding Xiaoxiang’s PR: mkj/dropbear#437 |
|
Guys, I opened the following discussion in the dropbear's repo: mkj/dropbear#440 |
Integrated SSH daemon authenticating against FSUTILS_PASSWD, with an ECDSA P-256 host key and an NSH session over a PTY per connection. Built from the upstream tarball with NuttX crypto and POSIX shims. Signed-off-by: Felipe Moura <moura.fmo@gmail.com>
Add README describing build, configuration and usage of the port. Signed-off-by: Felipe Moura <moura.fmo@gmail.com>
9c4ff8e to
af8bba4
Compare
mkj
left a comment
There was a problem hiding this comment.
Good work getting it going! I suspect there are probably a few more cleanup edge cases that need dealing with, upstream Dropbear relies on exit() cleanup a fair bit. At least most of the postauth code should be in your own dropbear_nshsession.c to handle specially there.
I've added a few notes about upstream parts.
I don't think UNUSED() changes make sense to go upstream. It seems like it would be simpler to #undef UNUSED somewhere in an appropriate nuttx-dropbear header file? (Not sure where the NuttX definition comes from)
| +#ifndef DROPBEAR_ECC_256 | ||
| #define DROPBEAR_ECC_256 (DROPBEAR_ECC) | ||
| +#endif | ||
| +#ifndef DROPBEAR_ECC_384 | ||
| #define DROPBEAR_ECC_384 (DROPBEAR_ECC) | ||
| +#endif | ||
| +#ifndef DROPBEAR_ECC_521 | ||
| #define DROPBEAR_ECC_521 (DROPBEAR_ECC) | ||
| +#endif |
| +#if DROPBEAR_SVR_REMOTETCPFWD | ||
| static int svr_cancelremotetcp(void); | ||
| static int svr_remotetcpreq(int *allocated_listen_port); | ||
| +#endif | ||
| +#if DROPBEAR_SVR_LOCALTCPFWD | ||
| static int newtcpdirect(struct Channel * channel); | ||
| +#endif |
There was a problem hiding this comment.
This has since changed upstream so might now be OK. mkj/dropbear#414
There was a problem hiding this comment.
The func(void) changes make sense to go upstream
|
|
||
| signal_pipe[0] = ses.signal_pipe[0]; | ||
| signal_pipe[1] = ses.signal_pipe[1]; | ||
| session_cleanup(); |
There was a problem hiding this comment.
session_cleanup() should be OK to clean up the session in the clean exit case (the fuzzer tests for leaks), but memory won't be freed when dropbear_exit() occurs for an error case. Those error cases can readily be triggered by network traffic.
Dropbear's fuzzing harness had the same problem. As a usable hack it sets DROPBEAR_TRACKING_MALLOC and then the harness calls m_malloc_free_epoch().
https://github.com/mkj/dropbear/blob/master/FUZZER-NOTES.md#malloc-wrapper
I don't want to add any more complexity/API like that to upstream Dropbear (it was never intended as a library), but you might be to patch something similar in the NuttX wrapper? If NuttX has arena allocators (or similar) that might be another option.
| #define dropbear_main dropbear_multi_entry | ||
| #include "dbutil.h" | ||
| #undef dropbear_main |
There was a problem hiding this comment.
| #define dropbear_main dropbear_multi_entry | |
| #include "dbutil.h" | |
| #undef dropbear_main | |
| #undef dropbear_main | |
| #include "dbutil.h" |
Hi @mkj thank you very much for your review and suggestions. I think undefining UNUSED macro and defining it again could work, but could we agree at least in moving the UNUSED from been used in the function prototype and move it to end of the function, like here: https://github.com/apache/nuttx/blob/master/drivers/syslog/syslog_channel.c#L304 This way works better for old compilers (that don't know about attribute((unused)) ), since NuttX is used for retro-computing as well (not only microcontrollers). |
|
I'm not keen on changing Dropbear's style. On other compilers Dropbear makes it a no-op. #ifdef UNUSED
#elif defined(__GNUC__)
# define UNUSED(x) UNUSED_ ## x __attribute__((unused))
#elif defined(__LCLINT__)
# define UNUSED(x) /*@unused@*/ x
#else
# define UNUSED(x) x
#endif |
@FelipeMdeO I try a porting in the last week but doesn't require so many patch/hack to dropbear code base. So could you split the real change require to port dropbear from the pure improvement? |
Summary
This PR adds two related changes that together bring up an SSH server
on the ESP32-C3 DevKit board using the Dropbear application:
boards/risc-v/esp32c3/esp32c3-devkit/configs/dropbear
A new
dropbeardefconfig is introduced for the ESP32-C3 DevKit board.It wires up the Dropbear SSH server application together with:
bring-up at boot).
/datamountpoint) to persist the host key andthe password database.
FSUTILS_PASSWDpointing to/data/passwdas the credential store,replacing a previous Dropbear-specific password-file path.
/data/dropbear_ecdsa_host_key.dropbeartask on every boot.sessions.
CONFIG_NETUTILS_DROPBEAR_STACKSIZEpinned to 65536 bytes; thedefault 32 KiB overflows during key exchange on this RISC-V target.
CONFIG_NETUTILS_DROPBEAR_LISTEN_RETRY_MAX=120so the daemon keepsretrying until the Wi-Fi link is fully up.
Wi-Fi credentials (
myssid/mypasswd) are placeholders and must beset via
menuconfigbefore flashing.crypto: expose ChaCha20 stream helpers
Dropbear uses the
chacha20-poly1305@openssh.comcipher, which requiresa stateful, multi-call ChaCha20 stream interface rather than the single-
block interface currently exposed by
crypto/chachapoly.c. Three helpersand a context struct are added:
struct chacha20_stream_ctx— opaque wrapper aroundchacha_ctx.chacha20_stream_setkey()— initialise the key.chacha20_stream_ivctr64()— set IV and 64-bit counter.chacha20_stream_crypt()— encrypt/decrypt an arbitrary-length buffer.All three functions are thin wrappers over the existing
chacha_*primitives; no new algorithm code is introduced.
Impact
dropbeardefconfig is additive anddoes not affect any existing configuration.
include/crypto/chachapoly.h. The change is purely additive; existingusers of
chacha20_setkey/chacha20_cryptare unaffected.CONFIG_NETUTILS_DROPBEAR.under
/data; they are generated at first run and persist acrossreboots. Wi-Fi credentials must be provisioned by the user before
flashing.
Testing
Host: Linux x86_64, GCC RISC-V toolchain
Board: ESP32-C3 DevKit (rev 0.4)
Build:
First-time user provisioning (serial console):
The NuttX passwd file lives on SPIFFS (
/data/passwd) and is empty on afresh flash. Before the first SSH login, create a user from the NSH
serial console:
The ECDSA host key is generated automatically on first boot.
Boot log shows Dropbear listening after Wi-Fi association:
SSH connection from the host: